{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## 聚类算法\n",
    "&emsp;&emsp;上篇主要介绍了一种机器学习的通用框架——集成学习方法，首先从准确性和差异性两个重要概念引出集成学习“**好而不同**”的四字真言，接着介绍了现阶段主流的三种集成学习方法：AdaBoost、Bagging及Random Forest，AdaBoost采用最小化指数损失函数迭代式更新样本分布权重和计算基学习器权重，Bagging通过自助采样引入样本扰动增加了基学习器之间的差异性，随机森林则进一步引入了属性扰动，最后简单概述了集成模型中的三类结合策略：平均法、投票法及学习法，其中Stacking是学习法的典型代表。本篇将讨论无监督学习中应用最为广泛的学习算法——聚类。  \n",
    "&emsp;&emsp;聚类是一种经典的**无监督学习**方法，**无监督学习的目标是通过对无标记训练样本的学习，发掘和揭示数据集本身潜在的结构与规律**，即不依赖于训练数据集的类标记信息。聚类则是试图将数据集的样本划分为若干个互不相交的类簇，从而每个簇对应一个潜在的类别。  \n",
    "&emsp;&emsp;聚类直观上来说是将相似的样本聚在一起，从而形成一个**类簇（cluster）**。那首先的问题是如何来**度量相似性**（similarity measure）呢？这便是**距离度量**，在生活中我们说差别小则相似，对应到多维样本，每个样本可以对应于高维空间中的一个数据点，若它们的距离相近，我们便可以称它们相似。那接着如何来评价聚类结果的好坏呢？这便是**性能度量**，性能度量为评价聚类结果的好坏提供了一系列有效性指标。\n",
    "\n",
    "### 距离度量\n",
    "&emsp;&emsp;谈及距离度量，最熟悉的莫过于欧式距离了，从年头一直用到年尾的距离计算公式：即对应属性之间相减的平方和再开根号。度量距离还有其它的很多经典方法，通常它们需要满足一些基本性质：\n",
    "\n",
    "- 非负性：$\\text{dist}(x_i,x_j) \\geqslant 0$；  \n",
    "- 同一性：$\\text{dist}(x_i,x_j)=0$当且仅当$x_i=x_j$；  \n",
    "- 对称性：$\\text{dist}(x_i,x_j)=dist(x_j,x_i)$  \n",
    "- 直递性（三角不等式：两边之和大于第三边）：$\\text{dist}(x_i,x_j) \\leqslant \\text{dist}(x_i,x_k) + \\text{dist}(x_k,x_j)$\n",
    "\n",
    "最常用的距离度量方法是**“闵可夫斯基距离”（Minkowski distance)**：$$\n",
    "\\text{dist}_{\\text{mk}}(x_i, x_j)=\\left(\\sum_{u=1}^n|x_{i u}-x_{j u}|^p \\right)^{\\frac{1}{p}}$$当p=1时，闵可夫斯基距离即**曼哈顿距离（Manhattan distance）**：$$\n",
    "\\text{dist}_{\\text{man}}(x_i,x_j)=\\|x_i-x_j\\|_{1}=\\sum_{u=1}^n|x_{i u}-x_{j u}|$$当p=2时，闵可夫斯基距离即**欧氏距离（Euclidean distance）**：$$\n",
    "\\text{dist}_{\\text{ed}}(x_i,x_j)=\\|x_i-x_j\\|_2=\\sqrt{\\sum_{u=1}^n|x_{i u}-x_{j u}|^2}$$&emsp;&emsp;我们知道属性分为两种：**连续属性**和**离散属性**（有限个取值）。对于连续值的属性，一般都可以被学习器所用，有时会根据具体的情形作相应的预处理，例如：归一化等；而对于离散值的属性，需要作下面进一步的处理：\n",
    "\n",
    "> 若属性值之间**存在序关系**，则可以将其转化为连续值，例如：身高属性“高”“中等”“矮”，可转化为$\\{1, 0.5, 0\\}$。  \n",
    "若属性值之间**不存在序关系**，则通常将其转化为向量的形式，例如：性别属性“男”“女”，可转化为$\\{(1,0),(0,1)\\}$。\n",
    "\n",
    "&emsp;&emsp;在进行距离度量时，易知**连续属性和存在序关系的离散属性都可以直接参与计算**，因为它们都可以反映一种程度，我们称其为“**有序属性**”；而对于不存在序关系的离散属性，我们称其为：“**无序属性**”，显然无序属性再使用闵可夫斯基距离就行不通了。  \n",
    "&emsp;&emsp;**对于无序属性，我们一般采用VDM进行距离的计算**，例如：对于离散属性的两个取值$a$和$b$，定义：$$\\text{VDM}_{p}(a, b)=\\sum_{i=1}^k\\left|\\frac{m_{u, a, i}}{m_{u, a}}-\\frac{m_{u, b, i}}{m_{u, b}}\\right|^p$$其中$i$表示类簇。  \n",
    "&emsp;&emsp;于是，在计算两个样本之间的距离时，我们可以将闵可夫斯基距离和VDM混合在一起进行计算：$$\\text{MinkovDM_p(x_i,x_j)}=\\left(\\sum_{u=1}^{n_c} |x_{iu}-x_{ju}|^p - \\sum_{u=n_c+1}^n \\text{VDM}_p(x_{iu},x_{ju}) \\right)^{\\frac{1}{p}}$$其中$\\displaystyle \\sum_{u=1}^{n_c} |x_{iu}-x_{ju}|^p$表示有序属性，$\\displaystyle \\sum_{u=n_c+1}^n \\text{VDM}_p(x_{iu},x_{ju})$表示无序属性。  \n",
    "&emsp;&emsp;若我们定义的距离计算方法是用来度量相似性，例如下面将要讨论的聚类问题，即距离越小，相似性越大，反之距离越大，相似性越小。这时距离的度量方法并不一定需要满足前面所说的四个基本性质，这样的方法称为：**非度量距离（non-metric distance）**。\n",
    "\n",
    "### 性能度量\n",
    "&emsp;&emsp;由于聚类算法不依赖于样本的真实类标，就不能像监督学习的分类那般，通过计算分对分错（即精确度或错误率）来评价学习器的好坏或作为学习过程中的优化目标。一般聚类有两类性能度量指标：**外部指标**和**内部指标**。\n",
    "\n",
    "#### 外部指标\n",
    "&emsp;&emsp;即将聚类结果与某个参考模型的结果进行比较，**以参考模型的输出作为标准，来评价聚类好坏**。假设聚类给出的结果为$\\lambda$，参考模型给出的结果是$\\lambda^*$，则我们将样本进行两两配对，定义：  \n",
    "$a=|SS|,SS=\\{(x_i,x_j) | \\lambda_i = \\lambda_j, \\lambda_i^* = \\lambda_j^*, i<j \\}$，参考结果同类簇，聚类结果同类簇   \n",
    "$b=|SD|,SD=\\{(x_i,x_j) | \\lambda_i = \\lambda_j, \\lambda_i^* \\neq \\lambda_j^*, i<j \\}$，参考结果不同类簇，聚类结果同类簇    \n",
    "$c=|DS|,DS=\\{(x_i,x_j)| \\lambda_i \\neq \\lambda_j, \\lambda_i^* = \\lambda_j^*, i<j \\}$，参考结果同类簇，聚类结果不同类簇  \n",
    "$d=|DD|,DD=\\{(x_i,x_j)| \\lambda_i \\neq \\lambda_j, \\lambda_i^* \\neq \\lambda_j^*, i<j \\}$，参考结果不同类簇，聚类结果不同类簇  \n",
    "&emsp;&emsp;显然$a$和$b$代表着聚类结果好坏的正能量，$b$和$c$则表示参考结果和聚类结果相矛盾，基于这四个值可以导出以下常用的外部评价指标（**取值范围$(0,1)$，取值越大越好。**）：\n",
    "\n",
    "- Jaccard系数（Jaccard Coefficient，简称JC）$$\\text{JC}=\\frac{a}{a+b+c}$$\n",
    "- FM指数（Fowlkes and Mallows Index，简称FMI）$$\\text{FMI}=\\sqrt{\\frac{a}{a+b} \\cdot \\frac{a}{a+c}}$$\n",
    "- Rand指数（Rand Index，简称RI）$$\\text{RI}=\\frac{2(a+d)}{m(m-1)}$$\n",
    "\n",
    "#### 内部指标\n",
    "&emsp;&emsp;内部指标即不依赖任何外部模型，直接对聚类的结果进行评估，聚类的目的是想将那些相似的样本尽可能聚在一起，不相似的样本尽可能分开，直观来说：**簇内高内聚紧紧抱团，簇间低耦合老死不相往来**。定义：\n",
    "\n",
    "- $\\displaystyle \\text{avg}(C)=\\frac{2}{|C|(|C|-1)}\\sum_{1 \\leqslant i < j \\leqslant |C|} \\text{dist}(x_i,x_j)$，**簇内平均距离，越小越好**  \n",
    "- $\\displaystyle \\text{diam}(C)=\\max_{1 \\leqslant i < j \\leqslant |C|} \\text{dist}(x_i,x_j)$，**簇内最大距离，越小越好**  \n",
    "- $\\displaystyle d_{\\min}(C_i,C_j)=\\min_{x_i \\in C_i, x_j \\in C_j} \\text{dist}(x_i,x_j)$，**簇间最小距离，越大越好**  \n",
    "- $\\displaystyle d_{\\text{cen}}(C_i,C_j)=\\text{dist}(\\mu_i,\\mu_j)$，**簇中心距离，越大越好**  \n",
    "\n",
    "基于上面的四个距离，可以导出下面这些常用的内部评价指标：\n",
    "\n",
    "- DB指数（Davies-Bouldin Index，简称DBI），**越小越好**$$\n",
    "\\text{DBI}=\\frac{1}{k} \\sum_{i=1}^k \\max _{j \\neq i}\\left(\\frac{\\text{avg}(C_i)+\\text{avg}(C_j)}{d_{\\text{cen}}(\\mu_i, \\mu_j)}\\right)\n",
    "$$，  \n",
    "- Dunn指数（Dunn Index，简称DI），**越大越好**$$\\text{DI}=\\min _{1 \\leqslant i \\leqslant k}\\left\\{\\min _{j \\neq i}\\left(\\frac{d_{\\min }(C_i, C_j)}{\\displaystyle \\max_{1 \\leqslant l \\leqslant k} \\text{diam}(C_l)}\\right)\\right\\}$$\n",
    "\n",
    "### 原型聚类\n",
    "&emsp;&emsp;原型聚类即“**基于原型的聚类**”（prototype-based clustering），原型表示模板的意思，就是通过参考一个模板向量或模板分布的方式来完成聚类的过程，常见的K-Means便是基于簇中心来实现聚类，混合高斯聚类则是基于簇分布来实现聚类。\n",
    "\n",
    "#### K-Means\n",
    "&emsp;&emsp;K-Means的思想十分简单，**首先随机指定类中心，根据样本与类中心的远近划分类簇，接着重新计算类中心，迭代直至收敛**。但是其中迭代的过程并不是主观地想象得出，事实上，若将样本的类别看做为“隐变量”（latent variable），类中心看作样本的分布参数，这一过程正是通过**EM算法**的两步走策略而计算出，其根本的目的是为了最小化平方误差函数E：$$E=\\sum_{i=1}^k \\sum_{x \\in C_i}\\|x-\\mu_i\\|_2^2$$&emsp;&emsp;K-Means的算法流程如下所示：\n",
    "\n",
    "> 输入：训练集$D=\\{x_1,x_2,\\ldots,x_m\\}$；  \n",
    "&emsp;&emsp;&emsp;聚类簇数$k$；  \n",
    "过程：  \n",
    "&nbsp;&nbsp;1: 从$D$中随机选择$k$个样本作为初始均值向量$\\{\\mu_1,\\mu_2,\\ldots,\\mu_k\\}$  \n",
    "&nbsp;&nbsp;2: **repeat**    \n",
    "&nbsp;&nbsp;3: &nbsp;&nbsp;&nbsp;&nbsp;令$C_i=\\emptyset(1 \\leqslant i \\leqslant k)$  \n",
    "&nbsp;&nbsp;4: &nbsp;&nbsp;&nbsp;&nbsp; **for** $j=1,2,\\ldots,m$ **do**  \n",
    "&nbsp;&nbsp;5: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;计算样本$x_i$与各均值向量$\\mu_i(1 \\leqslant i \\leqslant k)$的距离：$d_{ji}=\\|x_i-\\mu_i\\|_2$    \n",
    "&nbsp;&nbsp;6: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;根据距离最近的均值向量确定$x_j$的簇标记：$\\lambda_j=\\underset{i \\in (1,2,\\ldots,k)} {\\arg \\min} d_{ji}$    \n",
    "&nbsp;&nbsp;7: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将样本$x_i$划入相应的簇：$C_{\\lambda_j}=C_{\\lambda_j} \\cup \\{x_j\\}$    \n",
    "&nbsp;&nbsp;8:  &nbsp;&nbsp;&nbsp;&nbsp;**end for**  \n",
    "&nbsp;&nbsp;9:  &nbsp;&nbsp;&nbsp;&nbsp;**for** $i=1,2,\\ldots,k$ **do**  \n",
    "&nbsp;&nbsp;10: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;计算新均值向量：$\\displaystyle \\mu'_i = \\frac{1}{|C_i|} \\sum_{x \\in C_i} x$  \n",
    "&nbsp;&nbsp;11: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; **if** $\\mu'_i \\neq \\mu_i$ **then**  \n",
    "&nbsp;&nbsp;12: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将当前均值向量$\\mu_i$更新为$\\mu'_i$  \n",
    "&nbsp;&nbsp;13: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; **else**  \n",
    "&nbsp;&nbsp;14: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;保持当前均值向量不变  \n",
    "&nbsp;&nbsp;15: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;**end if**  \n",
    "&nbsp;&nbsp;16: &nbsp;&nbsp;&nbsp;&nbsp;**end for**  \n",
    "&nbsp;&nbsp;17: **until** 当前均值向量均为更新  \n",
    "输出：簇划分$C=\\{C_1,C_2,\\ldots,C_k\\}$\n",
    "\n",
    "#### 学习向量量化（LVQ）\n",
    "&emsp;&emsp;LVQ也是基于原型的聚类算法，与K-Means不同的是，**LVQ使用样本真实类标记辅助聚类**，首先LVQ根据样本的类标记，从各类中分别随机选出一个样本作为该类簇的原型，从而组成了一个**原型特征向量组**，接着从样本集中随机挑选一个样本，计算其与原型向量组中每个向量的距离，并选取距离最小的原型向量所在的类簇作为它的划分结果，再与真实类标比较。  \n",
    "> **若划分结果正确，则对应原型向量向这个样本靠近一些**  \n",
    "**若划分结果不正确，则对应原型向量向这个样本远离一些**\n",
    "\n",
    "LVQ算法的流程如下所示：\n",
    "> 输入：样本集$D=\\{(x_1,y_1),(x_1,y_1),\\ldots,(x_m,y_m)\\}$；  \n",
    "&emsp;&emsp;&emsp;原型向量个数$q$，各原型向量预设的类别标记$\\{t_1,t_2,\\ldots,t_q\\}$  \n",
    "&emsp;&emsp;&emsp;学习率$\\eta$  \n",
    "过程：  \n",
    "&nbsp;&nbsp;1: 初始化一组原型向量$\\{p_1,p_2,\\ldots,p_q\\}$  \n",
    "&nbsp;&nbsp;2: **repeat**    \n",
    "&nbsp;&nbsp;3: &nbsp;&nbsp;&nbsp;&nbsp;从样本集$D$随机选取样本$(x_j,y_j)$  \n",
    "&nbsp;&nbsp;4: &nbsp;&nbsp;&nbsp;&nbsp;计算样本$x_j$与$p_i(1 \\leqslant i \\leqslant q)$的距离：$d_{ij}=\\|x_j-p_i\\|_2$  \n",
    "&nbsp;&nbsp;5: &nbsp;&nbsp;&nbsp;&nbsp;找出与$x_j$距离最近的原型向量$p_{i^*}$，$\\displaystyle i^*=\\underset{i \\in \\{1,2,\\ldots,q\\}}{\\arg \\min} d_{ji}$    \n",
    "&nbsp;&nbsp;6: &nbsp;&nbsp;&nbsp;&nbsp;**if** $y_j = t_{i^*}$ **then**  \n",
    "&nbsp;&nbsp;7: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$p'=p_{i^*} + \\eta \\cdot (x_j - p_{i^*})$ **可以理解为类中心向$x$靠近**    \n",
    "&nbsp;&nbsp;8:  &nbsp;&nbsp;&nbsp;&nbsp; **else**  \n",
    "&nbsp;&nbsp;9:  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$p'=p_{i^*} - \\eta \\cdot (x_j - p_{i^*})$ **类中心向$x$远离**  \n",
    "10: &nbsp;&nbsp;&nbsp;&nbsp;**end if**  \n",
    "11: &nbsp;&nbsp;&nbsp;&nbsp;将原型向量$p_{i^*}$更新为$p'$  \n",
    "12: **until** 满足停止条件  \n",
    "输出：原型向量$\\{p_1,p_2,\\ldots,p_q\\}$\n",
    "\n",
    "#### 高斯混合聚类\n",
    "&emsp;&emsp;现在可以看出K-Means与LVQ都试图以类中心作为原型指导聚类，高斯混合聚类则采用高斯分布来描述原型。现假设**每个类簇中的样本都服从一个多维高斯分布，那么空间中的样本可以看作由k个多维高斯分布混合而成**。\n",
    "\n",
    "对于多维高斯分布，其概率密度函数如下所示：$$\n",
    "p(x)=\\frac{1}{(2 \\pi)^{\\frac{n}{2}}|\\Sigma|^{\\frac{1}{2}}} e^{-\\frac{1}{2}(x-\\mu)^T \\Sigma^{-1}(x-\\mu)}$$其中$\\mu$表示均值向量，$\\Sigma$表示协方差矩阵，可以看出一个多维高斯分布完全由这两个参数所确定。接着定义高斯混合分布为：$$\n",
    "p_M(x)=\\sum_{i=1}^k \\alpha_i \\cdot p(x | \\mu_i, \\Sigma_i)$$其中$\\alpha$称为混合系数，这样空间中样本的采集过程则可以抽象为：**（1）先选择一个类簇（高斯分布），（2）再根据对应高斯分布的密度函数进行采样**，这时候贝叶斯公式又能大展身手了：$$\\begin{aligned} p_M(z_j=i | x_j)\n",
    "&=\\frac{P(z_j=i) \\cdot p_M(x_j | z_j=i)}{p_M (x_j)} \\\\ \n",
    "&=\\frac{\\alpha_i \\cdot p(x_j | \\mu_i, \\Sigma_i)}{\\displaystyle \\sum_{l=1}^k \\alpha_l \\cdot p(x_j | \\mu_l, \\Sigma_l)}   \\end{aligned} \\tag{1}$$其中$p_{M}(z_j=i | x_j)$是类先验，$p_M(x_j | z_j=i)$是类条件。  \n",
    "&emsp;&emsp;此时只需要选择$P_M$最大时的类簇并将该样本划分到其中，看到这里很容易发现：这和那个传说中的贝叶斯分类不是神似吗，都是通过贝叶斯公式展开，然后计算类先验概率和类条件概率。但遗憾的是：**这里没有真实类标信息，对于类条件概率，并不能像贝叶斯分类那样通过最大似然法美好地计算出来**，因为这里的样本可能属于所有的类簇，这里的似然函数变为：$$L L(D)=\\ln \\left(\\prod_{j=1}^m p_M(x_j)\\right)=\\sum_{j=1}^m \\ln \\left(\\sum_{i=1}^k \\alpha_i \\cdot p(x_j | \\mu_i, \\Sigma_i)\\right)$$&emsp;&emsp;可以看出：简单的最大似然法根本无法求出所有的参数，这样$P_M$也就没法计算。**这里就要召唤出之前的EM大法，首先对高斯分布的参数及混合系数进行随机初始化，计算出各个$P_M$（即$\\gamma_{ji}$，第$i$个样本属于$j$类），再最大化似然函数（即LL（D）分别对$\\alpha、\\mu$和$\\Sigma$求偏导 ），对参数进行迭代更新**。$$\n",
    "\\mu_i=\\frac{\\displaystyle \\sum_{j=1}^m \\gamma_{j i} x_j}{\\displaystyle \\sum_{j=1}^m \\gamma_{j i}} \\\\\n",
    "\\alpha_{i}=\\frac{1}{m} \\sum_{j=1}^m \\gamma_{j i} \\\\\n",
    "\\Sigma_i=\\frac{\\displaystyle \\sum_{j=1}^m \\gamma_{j i}(x_j-\\mu_i)(x_j-\\mu_i)^T}{\\displaystyle \\sum_{j=1}^m \\gamma_{j i}}$$  \n",
    "高斯混合聚类的算法流程如下图所示：\n",
    "> 输入：样本集$D=\\{x_1,x_2,\\ldots,x_m\\}$；  \n",
    "&emsp;&emsp;&emsp;高斯混合成分个数$k$  \n",
    "过程：  \n",
    "&nbsp;&nbsp;1: 初始化高斯混合分布的模型参数$\\{(\\alpha_i,\\mu_i,\\Sigma_i) | 1 \\leqslant i \\leqslant k\\}$  \n",
    "&nbsp;&nbsp;2: **repeat**    \n",
    "&nbsp;&nbsp;3: &nbsp;&nbsp;&nbsp;&nbsp;**for** $j=1,2,\\ldots,m$ **do**  \n",
    "&nbsp;&nbsp;4: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;根据式（1）计算$x_i$由各混合成份生成的后验概率，即$\\gamma_{ji}=P_M(z_j=i | x_j) (1 \\leqslant i \\leqslant k)$ **E步**    \n",
    "&nbsp;&nbsp;5: &nbsp;&nbsp;&nbsp;&nbsp; **end for**  \n",
    "&nbsp;&nbsp;6: &nbsp;&nbsp;&nbsp;&nbsp; **for** $i=1,2,\\ldots,k$ **do** **M步**  \n",
    "&nbsp;&nbsp;7: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计算新均值向量：$\\mu'_i=\\frac{\\displaystyle \\sum_{j=1}^m \\gamma_{j i} x_j}{\\displaystyle \\sum_{j=1}^m \\gamma_{j i}}$  \n",
    "&nbsp;&nbsp;8: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;计算新协方差矩阵：$\\Sigma'_i=\\frac{\\displaystyle \\sum_{j=1}^m \\gamma_{j i}(x_j-\\mu_i)(x_j-\\mu_i)^T}{\\displaystyle \\sum_{j=1}^m \\gamma_{j i}}$  \n",
    "&nbsp;&nbsp;9: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;计算新混合系数：$\\displaystyle \\alpha'_{i}=\\frac{1}{m} \\sum_{j=1}^m \\gamma_{j i}$  \n",
    "10: &nbsp;&nbsp;&nbsp;&nbsp;**end for**   \n",
    "11: &nbsp;&nbsp;&nbsp;&nbsp;将模型参数$\\{(\\alpha_i, \\mu_i, \\Sigma_i) | 1 \\leqslant i \\leqslant k\\}$更新为$\\{(\\alpha'_i, \\mu'_i, \\Sigma'_i) | 1 \\leqslant i \\leqslant k\\}$  \n",
    "12: **until** 满足停止条件  \n",
    "13: $C_i = \\emptyset(1 \\leqslant i \\leqslant k)$    \n",
    "14: &nbsp;&nbsp;&nbsp;&nbsp;根据$\\lambda_j=\\underset{i \\in\\{1,2, \\ldots, k\\}}{\\arg \\max } \\gamma_{j i}$确定$x_i$的簇标记$\\lambda_j$  \n",
    "15: &nbsp;&nbsp;&nbsp;&nbsp;将$x_j$划入相应的簇：$C_{\\lambda_j} = C_{\\lambda_j} \\cup \\{x_j\\}$  \n",
    "16: **end for**  \n",
    "输出：簇划分$C=\\{C_1,C_2,\\ldots,C_k\\}$  \n",
    "\n",
    "### 密度聚类\n",
    "&emsp;&emsp;密度聚类则是基于密度的聚类，它从样本分布的角度来考察样本之间的可连接性，并基于可连接性（密度可达）不断拓展疆域（类簇）。其中最著名的便是**DBSCAN**算法，首先定义以下概念：\n",
    "> - $\\epsilon$-邻域：对$x_j \\in D$，其$\\epsilon$-邻域包含样本集$D$中与$x_j$的距离不大于$\\epsilon$的样本，即$N_{\\epsilon}(x_j)=\\{x_i \\in D | \\text{dist}(x_i, x_j) \\leqslant \\epsilon\\}$；  \n",
    "- 核心对象（core object）：若$x_j$的$\\epsilon$-邻域至少包含$MinPts$个样本，即$|N_{\\epsilon}(x_j)| \\geqslant MinPts$，则$x_j$是一个核心对象；  \n",
    "- 密度直达（directly density-reachable）（**必须在邻域中**）：若$x_j$位于$x_i$的$\\epsilon$-邻域中，且$x_i$是核心对象，则称$x_j$由$x_i$密度直达；  \n",
    "- 密度可达（density-reachable）（**即为传递性，并不一定要在邻域内**）：对$x_i$与$x_j$，若存在样本序列$p_1,p_2,\\ldots,p_n$，其中$p_1=x_i,p_n=x_j$，且$p_{i+1}$由$p_i$密度直达，则称$x_j$由$x_i$密度可达；  \n",
    "- 密度相连（density-connected）：对$x_i$与$x_j$，若存在$x_k$使得$x_i$与$x_j$均由$x_k$密度可达，则称$x_i$与$x_j$密度相连。 \n",
    "\n",
    "<br/><center>\n",
    "<img style=\"border-radius: 0.3125em;box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);\" src=\"../images/9-1-DBSCAN-Definition.png\"><br><div style=\"color:orange; border-bottom: 1px solid #d9d9d9;display: inline-block;color: #000;padding: 2px;\">图9-1 DBSCAN 定义的基本概念(MinPts=3) </div></center>\n",
    "\n",
    "&emsp;&emsp;简单来理解DBSCAN便是：**找出一个核心对象所有密度可达的样本集合形成簇**。首先从数据集中任选一个核心对象$A$，找出所有$A$密度可达的样本集合，将这些样本形成一个密度相连的类簇，直到所有的核心对象都遍历完。DBSCAN算法的流程如下图所示：\n",
    "\n",
    "> 输入：样本集$D=\\{x_1,x_2,\\ldots,x_m\\}$；  \n",
    "&emsp;&emsp;&emsp;邻域参数$(\\epsilon, MinPts)$   \n",
    "过程： \n",
    "&nbsp;&nbsp;1: 初始化核心对象集合：$\\Omega=\\emptyset$  \n",
    "&nbsp;&nbsp;2: **for** $j=1,2,\\ldots,m$ **do**  \n",
    "&nbsp;&nbsp;3: &nbsp;&nbsp;&nbsp;&nbsp;确定样本$x_j$的$\\epsilon$-邻域$N_{\\epsilon}(x_j)$  \n",
    "&nbsp;&nbsp;4: &nbsp;&nbsp;&nbsp;&nbsp;**if** $|N_{\\epsilon(x_j)}| \\geqslant MinPts$ **then**  \n",
    "&nbsp;&nbsp;5: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将样本$x_j$加入核心对象集合：$\\Omega=\\Omega \\cup \\{x_j\\}$  \n",
    "&nbsp;&nbsp;6: &nbsp;&nbsp;&nbsp;&nbsp; **end if**  \n",
    "&nbsp;&nbsp;7: **end for**  \n",
    "&nbsp;&nbsp;8: 初始化聚类簇数：$k=0$  \n",
    "&nbsp;&nbsp;9: 初始化未访问样本集合：$\\Gamma = D$  \n",
    "10: **while** $\\Omega \\neq \\emptyset$ **do**  \n",
    "11: &nbsp;&nbsp;&nbsp;&nbsp;记录当前未访问样本集合：$\\Gamma_{\\text{old}} = \\Gamma$；  \n",
    "12: &nbsp;&nbsp;&nbsp;&nbsp;随机选取一个核心对象$o \\in \\Omega$，初始化队列$Q=<o>$；  \n",
    "13: &nbsp;&nbsp;&nbsp;&nbsp;$\\Gamma = \\Gamma \\backslash \\{o\\}$；  \n",
    "14: **while** $Q \\neq \\emptyset$ **do**  \n",
    "15: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;取出队列$Q$中的首个样本$q$；  \n",
    "16: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;**if** $|N_{\\epsilon}(q)| \\geqslant MinPts$ **then** \n",
    "17: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;令$\\Delta=N_{\\epsilon}(q) \\cap \\Gamma$；  \n",
    "18: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将$\\Delta$中的样本加入队列$Q$；  \n",
    "19: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$\\Gamma = \\Gamma \\backslash \\Delta$；  \n",
    "20: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;**end if**  \n",
    "21: &nbsp;&nbsp;&nbsp;&nbsp;**end while**  \n",
    "22: &nbsp;&nbsp;&nbsp;&nbsp; $k=k+1$，生成聚类簇$C_k=\\Gamma_{\\text{old}} \\backslash \\Gamma$；  \n",
    "23: &nbsp;&nbsp;&nbsp;&nbsp;$\\Omega = \\Omega \\backslash C_k$  \n",
    "24: **end while**  \n",
    "输出：簇划分$C=\\{C_1,C_2,\\ldots,C_k\\}$\n",
    "\n",
    "其中1-7是找出所有核心对象，16是判定是否为核心对象，17是找出其邻域内的样本，即密度直达，22是核心对象$o$所有密度可达的样本集合，23是直到遍历完所有核心对象。\n",
    "\n",
    "### 层次聚类\n",
    "&emsp;&emsp;层次聚类是一种基于树形结构的聚类方法，常用的是**自底向上**的结合策略（**AGNES算法**）。假设有$N$个待聚类的样本，其基本步骤是：\n",
    "\n",
    "> 1. 初始化$\\rightarrow$把每个样本归为一类，计算每两个类之间的距离，也就是样本与样本之间的相似度；\n",
    "> 2. 寻找各个类之间最近的两个类，把他们归为一类（这样类的总数就少了一个）；\n",
    "> 3. 重新计算新生成的这个**类与各个旧类之间的相似度**；\n",
    "> 4. 重复2和3直到所有样本点都归为一类，结束。\n",
    "\n",
    "&emsp;&emsp;可以看出其中最关键的一步就是**计算两个类簇的相似度**，这里有多种度量方法：\n",
    "\n",
    "- 单链接（single-linkage）:取类间最小距离，$\\displaystyle d_{\\min }(C_i, C_j)=\\min_{x \\in C_i, x \\in C_j} \\text{dist}(x, z)$\n",
    "- 全链接（complete-linkage）:取类间最大距离，$\\displaystyle d_{\\max }(C_i, C_j)=\\max_{x \\in C_i, z \\in C_j} \\text{dist}(x, z)$\n",
    "- 均链接（average-linkage）:取类间两两的平均距离，$\\displaystyle d_{\\text{avg}}(C_i, C_j)=\\frac{1}{|C_i||C_j|} \\sum_{x \\in C_i} \\sum_{z \\in C_j} \\text{dist}(x, z)$\n",
    "\n",
    "&emsp;&emsp;很容易看出：**单链接的包容性极强，稍微有点暧昧就当做是自己人了，全链接则是坚持到底，只要存在缺点就坚决不合并，均链接则是从全局出发顾全大局**。层次聚类法的算法流程如下所示：\n",
    "\n",
    "> 输入：样本集$D=\\{x_1,x_2,\\ldots,x_m\\}$；  \n",
    "&emsp;&emsp;&emsp;聚类簇距离度量函数$d$；  \n",
    "&emsp;&emsp;&emsp;聚类簇数$k$；  \n",
    "过程： \n",
    "&nbsp;&nbsp;1: **for** $j=1,2,\\ldots,m$ **do**  \n",
    "&nbsp;&nbsp;2:&emsp;&emsp;$C_j=\\{x_j\\}$  \n",
    "&nbsp;&nbsp;3: **end for**  \n",
    "&nbsp;&nbsp;4: **for** $i=1,2,\\ldots,m$ **do**  \n",
    "&nbsp;&nbsp;5:&emsp;&emsp;**for** $j=1,2,\\ldots,m$ **do**  \n",
    "&nbsp;&nbsp;6:&emsp;&emsp;&emsp;&emsp;$M(i,j)=d(C_i,C_j)$；  \n",
    "&nbsp;&nbsp;7:&emsp;&emsp;&emsp;&emsp;$M(j,i)=M(i,j)$  \n",
    "&nbsp;&nbsp;8:&emsp;&emsp;**end for**  \n",
    "&nbsp;&nbsp;9: **end for**  \n",
    "10: 设置当前聚类簇个数：$q=m$  \n",
    "11: **while** $q > k$ **do**  \n",
    "12: &emsp;&emsp;找出距离最近的两个聚类簇$C_{i^*}$和$C_{j^*}$  \n",
    "13: &emsp;&emsp;合并$C_{i^*}$和$C_{j^*}$：$C_{i^*} = C_{i^*} \\cup C_{j^*}$  \n",
    "14: &emsp;&emsp;**for** $j=j^*+1,j^*+2,\\ldots,q$ **do**  \n",
    "15: &emsp;&emsp;&emsp;&emsp;将聚类簇$C_j$重编号为$C_{j-1}$  \n",
    "16: &emsp;&emsp;**end for**  \n",
    "17: &emsp;&emsp;删除距离矩阵$M$的第$j^*$行与第$j^*$列；  \n",
    "18: &emsp;&emsp;**for** $j=1,2,\\ldots,q-1$ **do**  \n",
    "19: &emsp;&emsp;&emsp;&emsp; $M(i^*,j)=d(C_{i^*},C_j)$；  \n",
    "20: &emsp;&emsp;&emsp;&emsp; $M(j,i^*)=M(i^*,j)$  \n",
    "21: &emsp;&emsp; **end for**  \n",
    "22: &emsp;&emsp;$q=q-1$  \n",
    "23: **end while**  \n",
    "输出：簇划分$C=\\{C_1,C_2,\\ldots,C_k\\}$\n",
    "\n",
    "&emsp;&emsp;在此，聚类算法就介绍完毕，分类/聚类都是机器学习中最常见的任务，我实验室的大Boss也是靠着聚类起家，从此走上人生事业钱途之巅峰，在书最后的阅读材料还看见Boss的名字，所以这章也是必读不可了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
